package cn.tedu.luban.worker.main.service;

import cn.tedu.luban.account.api.AccountApi;
import cn.tedu.luban.account.protocol.dto.AccountDTO;
import cn.tedu.luban.account.protocol.param.AccountCreateParam;
import cn.tedu.luban.attach.api.AttachApi;
import cn.tedu.luban.attach.protocol.dto.AttachDTO;
import cn.tedu.luban.attach.protocol.param.AttachUpdateParam;
import cn.tedu.luban.attach.protocol.query.AttachQuery;
import cn.tedu.luban.passport.protocol.jwt.UserPrinciple;
import cn.tedu.luban.passport.sdk.security.LubanSecurityContext;
import cn.tedu.luban.worker.common.enums.WorkerResultEnum;
import cn.tedu.luban.worker.main.repository.IWorkerAreaRepository;
import cn.tedu.luban.worker.main.repository.IWorkerAuditLogRepository;
import cn.tedu.luban.worker.main.repository.IWorkerCategoryRepository;
import cn.tedu.luban.worker.main.repository.IWorkerRepository;
import cn.tedu.luban.worker.protocol.bo.*;
import cn.tedu.luban.worker.protocol.param.AuditSubmitParam;
import cn.tedu.luban.worker.protocol.param.IdCardParam;
import cn.tedu.luban.worker.protocol.param.WorkerParam;
import cn.tedu.luban.worker.protocol.param.WorkerRegistParam;
import cn.tedu.luban.worker.protocol.query.WorkerListQuery;
import com.tedu.inn.protocol.ListRecordTotalBO;
import com.tedu.inn.protocol.exception.BusinessException;
import lombok.extern.slf4j.Slf4j;
import org.apache.dubbo.rpc.RpcException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

@Service
@Slf4j
public class WorkerService {
    /**
     * 师傅入驻 业务层
     * @param param attachList(上传2张图片) workerAreaList(选择区域) workerCategoryList(选择种类)
     *              realName idCard phone userId;
     * @return
     */
    //当前业务方法中包含多次写入数据到不同的表格和数据库 需要保证数据一致
    @Transactional(rollbackFor = Exception.class)//默认只对 RuntimeException和error回滚
    public Long workerCreate(WorkerRegistParam param) throws BusinessException {
        //1.获取当前登录的用户userId
        Long userId=getUserId();
        param.setUserId(userId);
        //2.验证身份证和人名 并且脱敏身份证信息
        checkIDCard(param);
        //3. 存储param中的师傅数据 worker
        Long workerId=saveWorker(param);
        //4. 存储param中的areas区域数据 worker_area 一次新增多条
        saveWorkerAreas(param);
        //5. 存储param中的categoris种类数据 worker_category 一次新增多条
        saveWorkerCategories(param);
        //6. 解析param中图片信息,绑定师傅业务数据
        bindPicture(param.getAttachList(),100,workerId.intValue());
        return workerId;
    }
    @Autowired
    private AttachApi attachApi;
    private void bindPicture(List<IdCardParam> attachList, int bizType, int bizId) throws BusinessException {
        //1. 根据业务 根据接口入参 封装 参数 业务绑定几张图片 封装几个元素
        List<AttachUpdateParam> attachParams=new ArrayList<>();
        for (IdCardParam idCardParam : attachList) {
            //创建绑定图片接口入参元素
            AttachUpdateParam attachParam=new AttachUpdateParam();
            //isCover 非空 businessType=bizType businessId=bizId id=idCardParam.getId()
            attachParam.setIsCover(1);
            attachParam.setBusinessType(bizType);
            attachParam.setBusinessId(bizId);
            attachParam.setId(idCardParam.getId());
            attachParams.add(attachParam);
        }
        //2. 当做本地worker有attachApi实现直接调用方法
        int result =0;
        try{
            result=attachApi.batchUpdateAttachByIdList(attachParams);
        }catch (RpcException e){
            //如果远程调用出现RPC异常,也属于调用失败
            log.error("师傅服务远程调用图片失败",e);
            throw new BusinessException("-9998","远程调用图片服务异常");
        }
        if (result==0){
            //说明对方的图片绑定 失败了
            log.error("图片绑定由于图片服务问题失败");
            throw new BusinessException("-9999","图片绑定失败");
        }
    }
    @Autowired
    private IWorkerRepository workerRepository;
    private Long saveWorker(WorkerRegistParam param) {
        //1. 利用师傅绑定userId 先删除历史数据 delete from worker where user_id=#{}
        workerRepository.delete(param.getUserId());
        //2. 新增师傅 insert into worker (字段) values (属性拼接)
        Long workerId=workerRepository.save(param);
        return workerId;
    }
    @Autowired
    private IWorkerCategoryRepository workerCategoryRepository;
    private void saveWorkerCategories(WorkerRegistParam param) {
        //1. delete
        workerCategoryRepository.delete(param.getUserId());
        //2. save
        workerCategoryRepository.saveWorkerCategories(param);
    }
    @Autowired
    private IWorkerAreaRepository workerAreaRepository;
    private void saveWorkerAreas(WorkerRegistParam param) {
        //1 使用user_id 绑定师傅的业务字段 做条件 删除新增时已有师傅区域
        workerAreaRepository.delete(param.getUserId());
        //2 新增提交的新参数 批量新增
        workerAreaRepository.saveWorkerAreas(param);
    }


    private void checkIDCard(WorkerRegistParam param) throws BusinessException {
        //验证 如果没有问题 param里的身份证就脱敏了
        //110111198810998098-->110***********8098
        log.info("检查身份信息的入参:{}",param);
    }

    private Long getUserId() throws BusinessException {
        UserPrinciple user = LubanSecurityContext.getLoginToken();
        //不一定能拿到登录用户.
        if (user==null){
            throw new BusinessException(WorkerResultEnum.USER_TOKEN_VERIFY_FAILED);
        }
        return user.getId();
    }

    public FrontWorkerBO workerDetail() throws BusinessException {
        //1.查询师傅表格使用的userId 从登录认证解析
        Long userId = getUserId();
        log.info("当前登录用户ID:{}",userId);
        //2.调用仓储层查询userI的师傅 单个对象的查询 仓储层出参也是BO
        FrontWorkerBO workerBO=workerRepository.findWorker(userId);
        //判断没有师傅数据 如果没有 不是null 而是属性为空的对象
        if (workerBO==null||workerBO.getId()==null){
            log.info("没有查询到师傅");
            //如果真是null 补一个对象
            if (workerBO==null){
                log.info("师傅为null,执行初始化赋值");
                workerBO =new FrontWorkerBO();
            }
            workerBO.setAuditStatus(3);
        }else{
            log.info("师傅不为空,数据:{}",workerBO);
            //3. 必定要补充 师傅区域 师傅种类
            putWorkerAreasAndCategories(workerBO);
            Integer auditStatus=workerBO.getAuditStatus();
            if (auditStatus==0){
                log.info("审核被驳回,填补拒绝原因");
                // 4. 拒绝原因 得使用第四个Repository  WorkerAuditRepository
                String reason=getLastestRejectReason(userId);
                workerBO.setRejectReason(reason);
            }else if(auditStatus==1){
                log.info("审核通过,查询账户余额");
                // 5. 账户累积余额 远程调用
                //5. 账户累积余额 远程调用 userId userName userPhone totalAmount
                AccountDTO account = accountApi.getAccount(userId);
                FrontAccountBO frontAccountBO=new FrontAccountBO();
                frontAccountBO.setTotalAmount(account.getTotalAmount());
                workerBO.setFrontAccountBO(frontAccountBO);

            }
        }
        log.info("查询到的师傅数据:{}",workerBO);
        return workerBO;
    }

    private String getLastestRejectReason(Long userId) throws BusinessException {
        log.info("当前查询驳回原因日志的师傅:{}",userId);
        //1. 调用仓储层 查询所有的userId下师傅审核记录
        List<AdminAuditBO> auditLogs = workerAuditLogRepository.findAuditLogs(userId);
        //2. 可能性 空 有通过记录 全是驳回记录
        if (CollectionUtils.isEmpty(auditLogs)){
            throw new BusinessException("1004","驳回原因为空");
        }else{
            //最后一个下标的元素 就是最新拒绝原因
            AdminAuditBO adminAuditBO = CollectionUtils.lastElement(auditLogs);
            //拼接原因字符串 rejectReson+ 详情描述: remark
            String rejectReason=adminAuditBO.getRejectReason()+"[详细原因:"+adminAuditBO.getRemark()+"]";
            return rejectReason;
        }
    }

    private void putWorkerAreasAndCategories(FrontWorkerBO workerBO) {
        //1. 共同使用一个userId
        Long userId = workerBO.getUserId();
        //2. areaRepo和categoryRepo查询对应数据向workerBO里封装
        List<WorkerAreaBO> areasBOS=workerAreaRepository.findAreas(userId);
        List<WorkerCategoryBO> categoryBOS=workerCategoryRepository.findCategories(userId);
        //3. workerBO 填充2个属性
        workerBO.setWorkerAreaBOList(areasBOS);
        workerBO.setWorkerCategoryBOList(categoryBOS);
    }
    //最少 查询师傅审核未通过的条件 audit_status in (0,2)
    public ListRecordTotalBO<AdminWorkerBO> workerAuditList(WorkerListQuery query) {
        //1. query中 由于前端问题 缺少默认参数  auditStatus=[0,2]
        putAuditStatusForUnApproved(query);
        //2. 利用query携带条件 非空属性 查询total
        Long total=workerRepository.countWorker(query);
        ListRecordTotalBO<AdminWorkerBO> listTotal=null;
        //判断 是否是0
        if (total==0){
            //查不到 返回一个空分页数据 total=0 list=null
            listTotal=new ListRecordTotalBO<>(null,0L);
        }else{
            //3. 查到分页数据
            List<AdminWorkerBO> list=workerRepository.findPageWorkers(query);
            //4.区域数据 品类数据
            putWorkerAreasAndCategories(list);
            listTotal=new ListRecordTotalBO<>(list,total);
        }
        return listTotal;
    }
    private void putWorkerAreasAndCategories(List<AdminWorkerBO> list){
        // 对list做循环 每循环一个师傅数据 拿到userID 查询区域和种类 封装adminWorkerBo
        for (AdminWorkerBO adminWorkerBO : list) {
            Long userId=adminWorkerBO.getUserId();
            //select * from worker_area where user_id=#{}
            List<WorkerAreaBO> areas = workerAreaRepository.findAreas(userId);
            List<String> areaDetails=new ArrayList<>();
            for (WorkerAreaBO area : areas) {
                areaDetails.add(area.getAreaDetail());
            }
            //select * from worker_category where user_id=#{}
            List<WorkerCategoryBO> categories = workerCategoryRepository.findCategories(userId);
            List<String> categoryDetails=new ArrayList<>();
            for (WorkerCategoryBO categoryBO : categories) {
                categoryDetails.add(categoryBO.getCategoryDetail());
            }
            //放到师傅bo
            adminWorkerBO.setAreaDetails(areaDetails);
            adminWorkerBO.setCategoryDetails(categoryDetails);
        }
    }
    private void putAuditStatusForUnApproved(WorkerListQuery query) {
        List<Integer> auditStatus = query.getAuditStatus();
        //如果入参query auditStatus是空的 才补充默认
        if(CollectionUtils.isEmpty(auditStatus)) {
            auditStatus.add(0);
            auditStatus.add(2);
        }
        log.info("补充完后 query值:{}",query);
    }

    @Autowired
    private IWorkerAuditLogRepository workerAuditLogRepository;
    public AdminWorkerBO auditDetail(Long userId) throws BusinessException {
        //1. 使用userId先查询adminWorkerBO TODO
        AdminWorkerBO workerBO=workerRepository.findAdminWorker(userId);
        //判断
        if (workerBO==null&&workerBO.getId()==null){
            throw new BusinessException("1002","师傅为空 请重新查询");
        }
        //2,3.补充 这个bo里的areas 和categories
        putWorkerAreasAndCategories(workerBO);
        //4. 查询当前师傅的审核列表 历史记录 select * from worker_audit_log where user_id=#{} order by gmt_create TODO
        List<AdminAuditBO> auditLogs=workerAuditLogRepository.findAuditLogs(userId);
        workerBO.setWorkerAuditLogs(auditLogs);
        //5. 图片查询 bizType=100 bizId=workerId 拿不到url 需要业务拼接 TODO
        putPictureUrls(workerBO);
        return workerBO;
    }

    private void putWorkerAreasAndCategories(AdminWorkerBO bo) {
        List<AdminWorkerBO> bos=new ArrayList<>();
        bos.add(bo);
        this.putWorkerAreasAndCategories(bos);
    }
    @Value("${attach.url-prefix}")
    private String urlPrefix;
    private void putPictureUrls(AdminWorkerBO workerBO) throws BusinessException {
        AttachQuery attachQuery=new AttachQuery();
        //bizType bizId
        attachQuery.setBusinessType(100);
        attachQuery.setBusinessId(workerBO.getId().intValue());
        List<AttachDTO> dtos = attachApi.getAttachInfoByParam(attachQuery);
        //当初师傅绑定了几张图片,dtos就有几个元素 循环每一个元素 拿到fileUuid 根据当前环境local 拼接url
        List<String> urls=new ArrayList<>();
        if (CollectionUtils.isEmpty(dtos)){
            //根据入驻申请师傅的代码逻辑,出现空图片 异常情况
            throw new BusinessException("1005","师傅图片为空");
        }else{
            for (AttachDTO dto : dtos) {
                String fileUuid=dto.getFileUuid();
                String url="http://localhost:8092/static/"+fileUuid;
                urls.add(url);
            }
        }
        //给最终查询workerBO填补attach信息
        workerBO.setAttachInfo(urls);
    }
    @Autowired
    private AccountApi accountApi;
    public Long auditSubmit(AuditSubmitParam param) throws BusinessException {
        //1. 从param拿到workerId 实际userId 查询师傅bo数据
        Long userId = param.getWorkerId();
        AdminWorkerBO adminWorker = workerRepository.findAdminWorker(userId);
        //判断是否为空
        if (adminWorker==null||adminWorker.getId()==null){
            //抛出异常结束
            throw new BusinessException("1002","师傅为空 请重新查询");
        }
        //2. 师傅不为空 获取当前师傅状态 判断流转是否正常
        Integer currentAuditStatus=adminWorker.getAuditStatus();
        Integer targetAuditStatus=param.getAuditStatus();
        checkAuditStatusTransfer(currentAuditStatus,targetAuditStatus);
        //3. 新增审核日志记录 worker_audit_log insert TODO
        param.setUserName(adminWorker.getRealName());
        Long auditId=workerAuditLogRepository.save(param);
        //4. 修改师傅审核状态 worker update worker set audit_status=#{} where user_id=#{userId}
        //单独为师傅业务中所需的 仓储入参 封装了一个WorkerParam TODO
        WorkerParam workerParam=new WorkerParam();
        workerParam.setAuditStatus(targetAuditStatus);
        workerParam.setUserId(userId);
        workerRepository.updateAuditStatus(workerParam);
        //判断审核提交值是否是1 师傅的cert_status是否是0
        Integer certStatus = adminWorker.getCertStatus();
        if (targetAuditStatus==1&&certStatus==0){
            //满足给师傅创建账户的情况
            //5.修改师傅的certStatus 1
            workerParam.setCertStatus(1);
            workerRepository.updateCertStatus(workerParam);
            //5.远程调用account创建 一个该师傅绑定账号
            AccountCreateParam accountCreateParam=new AccountCreateParam();
            accountCreateParam.setUserId(userId);
            accountCreateParam.setUserName(adminWorker.getRealName());
            accountCreateParam.setUserPhone(adminWorker.getPhone());
            accountApi.createAccount(accountCreateParam);
            Long account = accountApi.createAccount(accountCreateParam);
            System.out.println("创建账号,结果:"+account);
        }
        return auditId;
    }

    private void checkAuditStatusTransfer(Integer currentAuditStatus, Integer targetAuditStatus) throws BusinessException {
        //1-0 false 1-2 false 1-1 false 2-0 true 2-2 true 2-1 true 0-0 true 0-1 true 0-2 true
        //只要状态是通过 就不允许提交审核
        if (currentAuditStatus==1){
            throw new BusinessException("1003","审核状态流转失败");
        }
    }
}
